/*
 * Created on May 31, 2004
 *
 * Vectrics Inc.  GNU Public License.
 */
package org.vectrics.hibernate;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.type.Type;
import org.vectrics.DataException;
import org.vectrics.SystemException;



/**
 * @author mmoore
 */
public class SelectorImpl implements Selector {
	private transient final Log log = LogFactory.getLog(this.getClass());
	private short conjunction = Selector.CONJUNTION_AND;
	private List elements = new ArrayList();
	private Selector parent = null;
	private int nextParameterId = 0;
	private String name = "Unnamed selector";
	
	/**
	 * 
	 */
	public SelectorImpl() {
		super();
	}

	public SelectorImpl(String name) {
		super();
	}

	public Selector addChildSelector(short conjunction) {
		return this.addChildSelector("unnamed-selector", conjunction);
	}
	
	public Selector addChildSelector(String name, short conjunction) {
		SelectorImpl child = new SelectorImpl(name);
		child.parent = this;
		child.setConjuntion(conjunction);
		elements.add(child);
		return(child);
	}
	
	/* (non-Javadoc)
	 * @see org.vectrics.hibernate.Selector#setConjuntion(short)
	 */
	public void setConjuntion(short value) {
		conjunction = value;
	}

	public int getNextParameterId() {
		if (parent != null)
			return(parent.getNextParameterId());
		else {
			int nextId = this.nextParameterId;
			this.nextParameterId++;
			return(nextId);
		}
	}
	
	public List getElements() {
		return(elements);
	}
	
	
	public void addParametersToQuery(Query query) {
		log.debug("addParametersToQuery() - " + this.getIdentifier());
		Iterator iter = this.elements.iterator();
		while (iter.hasNext()) {
			SelectorElement element = (SelectorElement)iter.next();
			if (element instanceof SelectorCondition) {
				SelectorCondition condition = (SelectorCondition)element;
				if (condition.getParameter() != null) {
					String paramName = condition.getParameter().getName();
					Type paramType = condition.getParameter().getType();
					Object paramObject = condition.getParameter().getObject();
					
					if (paramName == null)
						throw new DataException("Null param name found");
					if (paramType == null)
						try {
							if (log.isDebugEnabled())
								log.debug("adding unknown parameter type to query: " + paramName +  "   value = " + paramObject);
							query.setParameter(paramName, paramObject);
						} catch (Exception he) {
							throw new DataException("Hibernate exception in" + getIdentifier(), he);
						}
					else {
						if (log.isDebugEnabled())
							log.debug("adding known parameter type to query: " + paramName +  "   value = " + paramObject);
						query.setParameter(paramName, paramObject, paramType);
					}
				}
			} else if (element instanceof Selector) {
				Selector childSelector = (Selector)element;
				if (childSelector.getElements().size() == 1) {
					Object subElement = childSelector.getElements().iterator().next();
					if (subElement instanceof Selector) {
						log.debug("The only element of this selector is a child selector, make the child the new main selector");
						childSelector = (Selector)subElement;
					}
				}
				childSelector.addParametersToQuery(query);
			} else {
				throw new DataException("Unexpected type: " + element.getClass().getName() + "  In " + getIdentifier());
			}
		}
	}

	public String getIdentifier() {
		String str = "";
		if (parent == null) {
			str = "base";
		} else {
			int index = parent.getElements().indexOf(this);
			str = parent.getIdentifier() + "." + index; 
		}
		return(str);
	}
	
	public String getStatement() {
		String conditionSection = null;
		log.debug("getStatement() - " + this.getIdentifier());
		boolean foundEmptySelector = false;
		
		Iterator iter = elements.iterator();
		if (iter.hasNext()) {
			conditionSection = "";
			while (iter.hasNext()) {
				SelectorElement element = (SelectorElement)iter.next();
				if (element instanceof SelectorCondition) {
					log.debug("next element is a condition");
					SelectorCondition condition = (SelectorCondition)element;
					if (condition.getCondition() == null) {
						throw new DataException("Condition was null in " + this.getIdentifier());
					}
					if (elements.size() > 0)
						conditionSection += "(";
					conditionSection += condition.getCondition();
					if (elements.size() > 0)
						conditionSection += ")";
					
				} else if (element instanceof Selector) {
					log.debug("next element is a selector");
					
					Selector selector = (Selector)element;
					if (elements.size() > 0)
						conditionSection += "(";
					conditionSection += selector.getStatement();
					if (elements.size() > 0)
						conditionSection += ")";
					
				} else
					throw new DataException("Selector element class not recognized: " 
							+ element.getClass().getName() 
							+ "  In " + this.getIdentifier());
				
				if (iter.hasNext()) {
					if (this.conjunction == Selector.CONJUNTION_AND)
						conditionSection += " and ";
					else
						conditionSection += " or ";
				}
			}
			
			if (conditionSection.trim().length() <= 0)
				conditionSection = null;
			} else {
				throw new SystemException("Empty condition in filter, name = " + this.name);
			}
		
		if (log.isDebugEnabled()) {
			log.debug("finished getStatement() - " + this.getIdentifier() + "  Condition section = " + conditionSection);
		}
		return(conditionSection);
	}

	
    public void addColumnToColumn(String column1, String operator, String column2) {
        SelectorCondition element = new SelectorCondition();
		element.setCondition(column1 + " " + operator + " " + column2);
		elements.add(element);
    }
	
	
	public void addParameterCondition(String field, String operator, Long value) {
		String paramName = "param" + getNextParameterId();
		Parameter newParam = new Parameter(paramName, value, Hibernate.LONG);

		SelectorCondition element = new SelectorCondition();

		element.setParameter(newParam);
		element.setCondition(field + " " + operator + " :" + paramName);
		elements.add(element);
	}
	
	public void addParameterCondition(String field, String operator, String value) {
		String paramName = "param" + getNextParameterId();
		Parameter newParam = new Parameter(paramName, value, Hibernate.STRING);

		SelectorCondition element = new SelectorCondition();

		element.setParameter(newParam);
		element.setCondition(field + " " + operator + " :" + paramName);
		elements.add(element);
	}
	
	public void addParameterCondition(String field, boolean value) {
		addParameterCondition(field, new Boolean(value));
	}
	
	
	public void addParameterCondition(String field, Boolean value) {
		String paramName = "param" + getNextParameterId();
		Parameter newParam = new Parameter(paramName, value, Hibernate.BOOLEAN);

		SelectorCondition element = new SelectorCondition();

		element.setParameter(newParam);
		element.setCondition(field + " = :" + paramName);
		elements.add(element);
	    
	}
	
	public void addParameterCondition(String field, String operator, Date date) {
		if (date == null)
			throw new NullPointerException("addParameterCondition was passed a null object");
		String paramName = "param" + getNextParameterId();
		Parameter newParam = new Parameter(paramName, date, Hibernate.TIMESTAMP);

		SelectorCondition element = new SelectorCondition();
		int paramId = getNextParameterId();
		element.setParameter(newParam);
		element.setCondition(field + " " + operator + " :" + paramName);
		elements.add(element);
	}
	
	
	public void addCondition(String condition) {
		SelectorCondition element = new SelectorCondition();
		element.setCondition(condition);
		if (condition == null)
			throw new DataException("Null condition added in " + this.getIdentifier());
		elements.add(element);
	}

	/*
	public List getParameterTypes() {
		return(parameterTypes);
	}
	
	public List getParameterObjects() {
		return(parameterObjects);
	}
	*/
	/*
	public void buildParameterArrays() {
		parameterTypes = new ArrayList();
		parameterObjects = new ArrayList();
		Iterator iter = this.elements.iterator();
		while (iter.hasNext()) {
			SelectorElement element = (SelectorElement)iter.next();
			if (element instanceof SelectorCondition) {
				SelectorCondition condition = (SelectorCondition)element;
				if (condition.getParameter() != null) {
					Type paramType = condition.getParameter().getType();
					Object paramObject = condition.getParameter().getObject();
					if (paramType == null)
						throw new DataException("Parameter missing type");
					else {
						parameterTypes.add(paramType);
						parameterObjects.add(paramObject);
					}
				}
			} else if (element instanceof Selector) {
				Selector childSelector = (Selector)element;
				parameterTypes.addAll(childSelector.getParameterTypes());
				parameterObjects.addAll(childSelector.getParameterObjects());
			} else {
				throw new DataException("Unexpected type: " + element.getClass().getName());
			}
		}
	}
	*/
	
}
